﻿using System;
using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using LogSystem;
using Pathfinding;
using Pathfinding.RVO;
using UnityEngine;
using Yoozoo.Gameplay.RTS;
using Yoozoo.Mars.Got;
using Random = UnityEngine.Random;

namespace Yoozoo.Gameplay.City.AI
{
    public enum seekerState
    {
        none = 0,
        walk = 1,
        executing = 2,
    }
    public class CityAmbientNPCSeeker : MonoBehaviour
    {
#if UNITY_EDITOR
        // debug的uid
        // [NonSerialized]
        public int DEBUG_UID; 
#endif
        public Animator animator;
        public AnimationClip[] ClipArray;
        public ModelInstaning model;
        public Seeker seeker;
        public CityAmbientNPCAIPath aiPath;
        public RVOController rvoController;
        public RaycastModifier _raycastModifier;

        private OnPathDelegate _pathCallback;
        
        /// <summary>
        /// 是否使用房间内的寻路
        /// 注意，他不等同于是否在房间内，会有一些NPC在房间内，但是要在房间外周围寻路。
        /// </summary>
        public bool IsUseRoomNavigate { get; private set; }

        private CityRealAiContainerManagerBase _targetContainer;

        private CityNPCSeekerEventData eventData;
        private bool _isInTimer;
        private bool _isNeedReStart;

        public seekerState State => state;
        private seekerState state;
        private int _searchPashFailedIndex = 0;

        private Action _searchPathPartialCallback;
        private Action _searchPathErrorCallback;

        private bool _hasPathCalculated = false;
        private static Vector3 _tempVector = Vector3.zero;

        // 记录上一次OnPositionChanged的帧数
        private int lastPositionChangedFrameCount;

        
        private void Awake()
        {
            // 当加载过程中，切出了内城，父节点隐藏，Awake未执行，但是自己的逻辑却在跑，会引发异常。
            // 所以一个系统里要尽量少的使用Awake这些函数，最好能够保持只在系统入口点使用，所有的流程函数的调用自己精确控制。
            seeker = transform.GetComponent<Seeker>();
            seeker.startEndModifier.addPoints = false;
            seeker.startEndModifier.useRaycasting = false;
            seeker.startEndModifier.useGraphRaycasting = false;
            aiPath = transform.GetComponent<CityAmbientNPCAIPath>();
            rvoController = transform.GetComponent<RVOController>();
            animator = transform.GetComponentInChildren<Animator>();
            _raycastModifier = transform.GetComponent<RaycastModifier>();
            _pathCallback = PathCallback;
            if (animator != null)
            {
                ClipArray = animator.runtimeAnimatorController.animationClips;
                
            }
            else
            {
                ClipArray = Array.Empty<AnimationClip>();
            }
            model = transform.GetComponentInChildren<ModelInstaning>(true);
            state = seekerState.none;

            _doGetter = GetRotation;
            _doSetter = SetRotation;
            
            lastPositionChangedFrameCount = Time.frameCount - 1;
        }
        private void OnEnable()
        {
            if (!_isNeedReStart)
            {
                return;
            }

            _isNeedReStart = false;
            SetIsInRoomAction(IsUseRoomNavigate);
            Timers.inst.CallLater((o) =>
            {
                if (eventData.IsValid)
                {
                    if (eventData.TimeType == (int)CityRealEventTimeType.MoveTime)
                    {
                        InRoomStartMove(eventData.Uid, eventData.BuildingId, eventData.TargetPos, eventData.Speed, _targetContainer, eventData.MoveCompleteCallback);
                    }
                    else
                    {
                        var eventConfig = CityRealAiManager.GetInstance().ConfigMgr.GetAiEventConfig(eventData.EventId);
                        InRoomStartMove(eventData.Uid, eventData.EventTreeId, eventData.TargetPos, eventData.TargetRot, _targetContainer, eventConfig, eventData.MoveCompleteCallback, eventData.CompleteCallback, eventData.WaitFailedCallback);
                    }
                }
            });
        }
        private void OnDisable()
        {
            if (seeker != null)
            {
                seeker.pathCallback -= _pathCallback;
            }
            // 隐藏的时候移除所有小人任务
            if (aiPath)
            {
                aiPath.isStopped = true;
                aiPath.updatePosition = false;
                aiPath.updateRotation = false;
                _isNeedReStart = true;
            }
        }

        void Start()
        {
            if (aiPath)
            {
                aiPath.onReached += OnTargetReached;
                aiPath.onChanged += OnPositionChanged;
            }
            if (rvoController)
            {
                rvoController.collidesWith = RVOLayer.DefaultAgent;
            }
        }

        private void PathCallback(Path p)
        {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.Log(LogModule.CityAI, $"Uid = {eventData.Uid} PathCallback state:{p.CompleteState}, distance:{aiPath.remainingDistance}, reached:{aiPath.reachedDestination}, path:{p.path.Count}");
#endif
            _hasPathCalculated = true;
            
            seeker.pathCallback -= _pathCallback;
            if (p.CompleteState == PathCompleteState.Error)
            {
                _searchPashFailedIndex++;
                if (_searchPashFailedIndex >= CityRealAiConst.MaxSearchPathFailedIndex)
                {
                    _searchPathErrorCallback?.Invoke();
                }
                else
                {
                    _searchPathPartialCallback?.Invoke();
                }
                
            }
            else if (p.CompleteState == PathCompleteState.Complete)
            {
                _searchPashFailedIndex = 0;
            }

            if (aiPath.remainingDistance < 0.1f || aiPath.reachedDestination)
            {
                _TargetReached(false);
            }
        }

        private void Update()
        {
            if (_isInTimer)
            {
                if (eventData.TimeType != (int)CityRealEventTimeType.MoveTime)
                {
                    if (Time.time >= eventData.EndTime)
                    {
                        _isInTimer = false;
                        if (eventData.TimeType == (int)CityRealEventTimeType.WaitTime)
                        {
                            ExecuteWaitFailed();
                        }
                        else
                        {
                            ExecuteComplete();
                        }
                    }
                }
                else
                {
                    _isInTimer = false;
                }
            }
        }

        private void OnDestroy()
        {
            OnReset();
            if (aiPath)
            {
                aiPath.onReached -= OnTargetReached;
                aiPath.onChanged -= OnPositionChanged;
            }

            if (seeker)
            {
                seeker.pathCallback -= PathCallback;
            }

            _searchPathPartialCallback = null;
            _searchPathErrorCallback = null;
        }

        public void SetupStartEndExactnessType(StartEndModifier.Exactness exactness)
        {
            if (!seeker)
            {
                return;
            }
            
            seeker.startEndModifier.exactStartPoint = exactness;
            seeker.startEndModifier.exactEndPoint = exactness;
        }

        //房间内小人的起始位置
        public void UpdatePos(Vector3 initPos)
        {
            transform.position = initPos;
        }

        public void SetupSearchPathFailedCallback(Action partialCallback, Action errorCallback)
        {
            _searchPathPartialCallback = partialCallback;
            _searchPathErrorCallback = errorCallback;
        }

        public void SetIsInRoomAction(bool isUseRoomNavigate)
        {
            IsUseRoomNavigate = isUseRoomNavigate;
            if (isUseRoomNavigate)
            {
                if (_raycastModifier != null)
                {
                    _raycastModifier.useGraphRaycasting = false;
                }

                if (aiPath != null) 
                {
                    aiPath.constrainInsideGraph = false;
                }

                if (seeker != null)
                {
                    seeker.graphMask = GraphMask.FromGraphName("RoomPointMap");
                }
            }
            else
            {
                if (_raycastModifier != null)
                {
                    _raycastModifier.useGraphRaycasting = true;
                }

                if (aiPath != null)
                {
                    aiPath.constrainInsideGraph = true;
                }

                if (seeker != null)
                {
                    seeker.graphMask = GraphMask.FromGraphName("RandomMap");
                }
            }
        }

        public float GetLengthByName(string clipName)
        {
            if (model)
            {
                return ((IModel)model).GetAnimationLength(clipName, 0.1f);
            }
            if (ClipArray == null)
            {
                return 0.1f;
            }
            for (int i = 0; i < ClipArray.Length; i++)
            {
                var clip = ClipArray[i];
                if (clip.name.Equals(clipName))
                {
                    return clip.length;
                }
            }

            return 0.1f;
        }
        
        public void InRoomStartMove(int uid, int roomId, Vector3 targetPos, float speed, CityRealAiContainerManagerBase targetContainer, Action moveCompleteCallback)
        {
            if (animator == null)
            {
                animator = transform.GetComponentInChildren<Animator>();
                if (animator != null)
                {
                    ClipArray = animator.runtimeAnimatorController.animationClips;
                }
            }
            OnReset();
            _targetContainer = targetContainer;
            eventData = new CityNPCSeekerEventData();
            eventData.IsValid = true;
            eventData.Uid = uid;
            eventData.BuildingId = roomId;
            eventData.GotoAni = "Walk";
            eventData.TargetPos = targetPos;
            eventData.TimeType = (int) CityRealEventTimeType.MoveTime;
            eventData.Speed = speed;
            eventData.MoveCompleteCallback = moveCompleteCallback;           
#if UNITY_EDITOR
            gameObject.name = $"AI Uid = {uid} 移动前往 = {roomId}";
#endif
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.LogFormat(LogModule.CityAI, "Uid = {0} 移动 房间或区域 = {1} 目标点 {2} world {3}", LogLevelType.Develop, eventData.Uid, roomId, targetPos, GetWorldPos(targetPos));
#endif  
             SetAIPathTarget(eventData);
        }
        
        public void InRoomStartMove(int uid, int eventTreeId, Vector3 targetPos, Vector3 targetRot, CityRealAiContainerManagerBase targetContainer, CityRealAiEventConfig eventConfig, Action moveCompleteCallback, Action completeCallback, Action waitFailedCallback)
        {
            if (animator == null)
            {
                animator = transform.GetComponentInChildren<Animator>();
                if (animator != null)
                {
                    ClipArray = animator.runtimeAnimatorController.animationClips;
                }
            }
            OnReset();
            _targetContainer = targetContainer;
            eventData = new CityNPCSeekerEventData();
            eventData.IsValid = true;
            eventData.Uid = uid;
            eventData.EventTreeId = eventTreeId;
            eventData.EventId = eventConfig.event_id;
            eventData.GotoAni = eventConfig.goto_ani;
            eventData.AniName = eventConfig.animation;
            eventData.TimeType = eventConfig.time_type;
            eventData.Speed = eventConfig.goto_speed;
            if (eventConfig.parameter == CityRealAiConst.NpcOutCityTag)
            {
                var cfgMgr = CityRealAiManager.GetInstance().ConfigMgr;
                var timeRange = cfgMgr.GlobalCfg.cityRealAiOutCityTimeRange;
                var timeRandom = cfgMgr.GlobalCfg.cityRealAiOutCityTimeRandom;
                var timeStep = cfgMgr.GlobalCfg.cityRealAiOutCityTimeStep;
                var leftRange = (timeRange - timeRandom) / timeStep;
                var rightRange = (timeRandom + timeRandom) / timeStep + 1;
                eventData.WaitSec = Mathf.Max(Random.Range(leftRange, rightRange) * timeStep, 1); // 最少隐藏1秒
            }
            else if (eventConfig.parameter == CityRealAiConst.NpcCheckTicketTimeTag)
            {
                eventData.WaitSec = CityRealAiConst.CheckTicketTime; // 检票时长
            }
            else
            {
                if (eventConfig.time_type == (int)CityRealEventTimeType.AutoTime)
                {
                    eventData.WaitSec = GetLengthByName(eventConfig.animation);
                }
                else if (eventConfig.time_type == (int)CityRealEventTimeType.NormalTime)
                {
                    eventData.WaitSec = eventConfig.execute_time;
                }
                else if (eventConfig.time_type == (int)CityRealEventTimeType.WaitTime)
                {
                    eventData.WaitSec = eventConfig.execute_time; // 等待事件时，为最大等待事件，时间结束则等待失败
                }
                else
                {
                    eventData.WaitSec = 0;
                }
            }
            eventData.WaitAni = eventConfig.wait_ani;
            eventData.TargetPos = targetPos;
            eventData.TargetRot = targetRot;
            eventData.MoveCompleteCallback = moveCompleteCallback;
            eventData.CompleteCallback = completeCallback;
            eventData.WaitFailedCallback = waitFailedCallback;
#if UNITY_EDITOR
            gameObject.name = $"AI Uid = {uid} TreeId = {eventTreeId} EventId = {eventConfig.event_id} pos = {GetWorldPos(targetPos)}";
#endif
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.LogFormat(LogModule.CityAI, "Uid = {0} EventTreeId = {1} EventId = {2} 目标点 {3} world: {4}", LogLevelType.Develop, eventData.Uid, eventData.EventTreeId, eventData.EventId, targetPos, GetWorldPos(targetPos));
#endif
            SetAIPathTarget(eventData);
        }
        
        
        
        /// <summary>
        /// 设置寻路终点
        /// </summary>
        private void SetAIPathTarget(CityNPCSeekerEventData data)
        {
            if (!gameObject.activeInHierarchy) // 处于隐藏状态时，只设置数据，等恢复为显示状态后，再继续执行。
            {
                return;
            }
            
            state = seekerState.walk;
            // 判断距离是否需要寻路
            var selfPos = transform.position;
            var self2DPos = new Vector3(selfPos.x, 0,selfPos.z);
            var targetPos = GetWorldPos(data.TargetPos);
            var target2DPos = new Vector3(targetPos.x, 0, targetPos.z);
            if (Vector3.Distance(self2DPos, target2DPos) < 0.1f)
            {
                _TargetReached(false);
                return;
            }
            var speedFactor = 1f;
            if (data.Speed > 0)
            {
                speedFactor = data.Speed;
            }
            var speed = CityRealAiConst.NpcDefaultMaxSpeed * speedFactor;

            if (animator)
            {
                animator.SetTrigger(data.GotoAni);
            }
            if (model)
            {
                model.SetTrigger(data.GotoAni);
                model.SetAnimatorSpeed(speedFactor);
            }
            _hasPathCalculated = false;
            aiPath.maxSpeed = speed;
            aiPath.destination = target2DPos;
            seeker.pathCallback += _pathCallback;
            aiPath.updatePosition = true;
            aiPath.updateRotation = true;
            aiPath.SearchPath();
            aiPath.isStopped = false;
        }
        
        public Vector3 GetWorldPos(Vector3 localPos)
        {
            if (_targetContainer == null)
            {
                return localPos;
            }

            return _targetContainer.GetWorldPos(localPos);
        }

        /// <summary>
        /// 到达目的地
        /// </summary>
        private void OnTargetReached()
        {
            _TargetReached(true);
        }

        void _TargetReached(bool checkPathCalculated)
        {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.Log(LogModule.CityAI, $"Uid = {eventData.Uid} _TargetReached checkPathCalculated:{checkPathCalculated}, _hasPathCalculated:{_hasPathCalculated}, ");
#endif
            
            if (state != seekerState.walk)
            {
                return;
            }

            if (checkPathCalculated && !_hasPathCalculated)
            {
                // 尚未算出路径
                return;
            }
            state = seekerState.executing;
            aiPath.isStopped = true;
            aiPath.updatePosition = false;
            aiPath.updateRotation = false;
            if (eventData.IsValid)
            {
                _tempVector = GetWorldPos(eventData.TargetPos);
                // transform.position = _tempVector;
                aiPath.FinalizeMovement(_tempVector, transform.rotation);
            }
             
            // 执行时间大于0才是
            if (eventData.TimeType == (int) CityRealEventTimeType.NormalTime || 
                eventData.TimeType == (int) CityRealEventTimeType.AutoTime)
            {
                eventData.EndTime = eventData.WaitSec + Time.time;
                _isInTimer = true;
                ExecuteStart();
            }
            else if (eventData.TimeType == (int) CityRealEventTimeType.WaitTime)// 等待事件，则播放等待动画
            {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
                // if (eventData != null)
                // {
                //     Debug.Log($"Uid = {eventData.Uid} _TargetReached SetTrigger:{eventData.WaitAni}");
                // }
#endif
                if (animator)
                {
                    animator.SetTrigger(eventData.WaitAni);
                }
                if (model)
                {
                    model.SetTrigger(eventData.WaitAni);
                    model.SetAnimatorSpeed(1);
                }
                if (eventData.WaitSec > 0)
                {
                    eventData.EndTime = eventData.WaitSec + Time.time;
                    _isInTimer = true;
                    ExecuteStart();
                }
                else
                {
                    _isInTimer = false;
                    ExecuteStart();
                }
            }
            else if (eventData.TimeType == (int) CityRealEventTimeType.MoveTime)
            {
                _isInTimer = false;
                ExecuteStart();
            }
            else
            {
                _isInTimer = false;
            }
        }

        public void PlayAni()
        {
            if (!string.IsNullOrEmpty(eventData.AniName))
            {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
                Debug.Log($"Uid = {eventData.Uid} PlayAni() SetTrigger:{eventData.AniName}");
#endif
                Rotate(eventData.TargetRot, 0.1f);
                if (animator)
                {
                    animator.SetTrigger(eventData.AniName);
                }
                if (model)
                {
                    model.SetTrigger(eventData.AniName);
                    model.SetAnimatorSpeed(1);
                }
            }
        }

        private DOGetter<Quaternion> _doGetter;
        private DOSetter<Quaternion> _doSetter;

        private Quaternion GetRotation()
        {
            return transform.rotation;
        }

        private void SetRotation(Quaternion rotation)
        {
            transform.rotation = rotation;
        }

        private void Rotate(Vector3 endValue, float duration)
        {
            var t = DOTween.To(_doGetter, _doSetter, endValue, duration);
            t.SetRecyclable(true);
            t.SetTarget(transform);
            t.plugOptions.rotateMode = RotateMode.Fast;
        }

        public void PlayAni(string aniName, Vector3 rotate)
        {
            if (!rotate.Equals(Vector3.zero))
            {
                Rotate(rotate, 0.1f);
            }
            if (!string.IsNullOrEmpty(aniName))
            {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
                Debug.Log($"Uid = {eventData.Uid} PlayAni(string aniName, Vector3 rotate) SetTrigger:{eventData.AniName}");
#endif
                if (animator)
                {
                    animator.SetTrigger(aniName);
                }
                if (model)
                {
                    model.SetTrigger(aniName);
                    model.SetAnimatorSpeed(1);
                }
            }
        }

        //执行事件开始
        private void ExecuteStart()
        {
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            if (eventData.TimeType == (int)CityRealEventTimeType.MoveTime)
            {
                Debug.LogFormat(LogModule.CityAI, "Uid = {0} 移动完成 房间或区域 = {1}", LogLevelType.Develop, eventData.Uid, eventData.BuildingId);
            }
            else
            {
                Debug.LogFormat(LogModule.CityAI, "Uid = {0} EventTreeId = {1} EventId = {2} 开始 执行事件 TimeType = {3} DelayTime = {4}", LogLevelType.Develop, eventData.Uid, eventData.EventTreeId, eventData.EventId, eventData.TimeType, eventData.WaitSec);
            }
#endif

            if (eventData.MoveCompleteCallback != null)
            {
                eventData.MoveCompleteCallback.Invoke();
            }
        }
        
        //执行事件结束
        private void ExecuteComplete()
        {
            state = seekerState.none;     
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.LogFormat(LogModule.CityAI, "Uid = {0} EventTreeId = {1} EventId = {2} 结束 执行事件 TimeType = {3} DelayTime = {4}", LogLevelType.Develop, eventData.Uid, eventData.EventTreeId, eventData.EventId, eventData.TimeType, eventData.WaitSec);
#endif
            if (eventData.CompleteCallback != null)
            {
                eventData.CompleteCallback.Invoke();
            }
        }

        private void ExecuteWaitFailed()
        {
            if (eventData.WaitFailedCallback != null)
            {
                eventData.WaitFailedCallback.Invoke();
            }
        }

        private void OnPositionChanged()
        {
            if (eventData.IsValid)
            {
                // 防止死循环，一帧只调用一次
                if (lastPositionChangedFrameCount == Time.frameCount)
                {
                    return;
                }
                lastPositionChangedFrameCount = Time.frameCount;
                
                SetAIPathTarget(eventData);
            }
#if UNITY_EDITOR && ENABLE_CITY_AI_PROFILER
            Debug.Log(LogModule.CityAI, $"Uid = {eventData.Uid} OnPositionChanged");
#endif
        }

        public void OnReset()
        {
            _isInTimer = false;

            eventData.IsValid = false;
            eventData.MoveCompleteCallback = null;
            eventData.CompleteCallback = null;
            eventData.WaitFailedCallback = null;

            state = seekerState.none;
            if (aiPath != null)
            {
                aiPath.isStopped = true;
                aiPath.updatePosition = false;
                aiPath.updateRotation = false;
            }

            if (seeker != null)
            {
                seeker.pathCallback -= _pathCallback;
            }

            _targetContainer = null;
        }
    }
}
